package com.catazzz.springbootstudy.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @作者: Tao
 * @日期: 2025/1/7
 * @时间: 16:44
 * @描述: <br><pre>
 */
@Slf4j
//@Component
public class RedisLock {

//    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 尝试获取分布式锁
     *
     * @param key   锁的键，通常为锁的唯一标识
     * @param value 锁的值，可以用于存储锁的一些额外信息，如锁定时间
     * @return 如果成功获取锁，则返回true；否则返回false
     */
    public boolean lock(String key, String value) {
        // 使用Redis的setIfAbsent功能来尝试设置键值对，如果键不存在则设置成功，表示获取锁成功
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));
    }

    /**
     * 尝试获取带有超时设置的分布式锁
     *
     * @param key     锁的键，通常为锁的唯一标识
     * @param value   锁的值，可以用于存储锁的一些额外信息，如锁定时间
     * @param timeOut 锁的超时时间（秒），避免死锁发生
     * @return 如果成功获取锁，则返回true；否则返回false
     */
    public boolean lockV2(String key, String value, long timeOut) {
        // 使用Redis的setIfAbsent功能来尝试设置键值对，并指定超时时间，如果键不存在则设置成功，表示获取锁成功
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, timeOut, TimeUnit.SECONDS));
    }

    /**
     * 尝试获取分布式锁
     * 该方法与lockV2相比，优化了锁获取逻辑，避免了死锁的发生
     *
     * @param key 锁的键，用于标识锁
     * @param value 锁的值，通常设置为当前时间加上超时时间，用于实现锁的自动过期
     * @return 返回是否成功获取锁
     */
    public boolean lockV3(String key, String value) {
        // 尝试设置键值对，如果键不存在则设置成功，表示获取锁成功
        if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value))){
            return true;
        }

        // 获取当前锁的值
        String oldValue = redisTemplate.opsForValue().get(key);
        // 检查当前锁是否已经过期
        if (Objects.nonNull(oldValue) && System.currentTimeMillis() > Long.parseLong(oldValue)){
            // 使用getAndSet方法更新锁的值，如果返回的旧值为空或者与当前锁的值相同，表示更新成功，获取锁成功
            String valueByGetAndSet = redisTemplate.opsForValue().getAndSet(key, value);
            if (Objects.isNull(valueByGetAndSet) || valueByGetAndSet.equals(oldValue)){
                return true;
            }
        }

        // 如果以上条件都不满足，表示获取锁失败
        return false;
    }



}
